package com.feiwo.nonego.rpc.provider.common.scanner;

import com.feiwo.nonego.rpc.common.annotation.RpcService;
import com.feiwo.nonego.rpc.common.scanner.ClassScanner;
import com.feiwo.nonego.rpc.common.util.RpcServiceUtils;
import com.feiwo.nonego.rpc.common.util.StringUtils;
import com.feiwo.nonego.rpc.protocol.meta.ServiceMeta;
import com.feiwo.nonego.rpc.registry.api.RegistryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author feiwo 2025-08-03
 */
public class RpcServiceScanner extends ClassScanner {

    private static final Logger LOGGER = LoggerFactory.getLogger(RpcServiceScanner.class);

    /**
     * 扫描指定包路径下标注了@RpcService注解的类，并记录相关信息
     *
     * @param scanPackage 需要扫描的包路径
     * @return 包含扫描到的服务处理器映射关系的Map
     * @throws IOException 当扫描过程中发生IO异常时抛出
     */
    public static Map<String, Object> doScannerRpcServiceAnnotation(String scanPackage) throws IOException {
        var handlerMap = new HashMap<String, Object>();
        // 获取指定包路径下的所有类名列表
        List<String> classNameList = getClassNameList(scanPackage);
        if (classNameList.isEmpty()) {
            return handlerMap;
        }

        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // 遍历类名列表，检查每个类是否标注了@RpcService注解
        for (String className : classNameList) {
            try {
                Class<?> clazz = Class.forName(className, false, classLoader);
                RpcService rpcService = clazz.getAnnotation(RpcService.class);
                if (rpcService != null) {
                    // 记录标注了注解的类的相关信息
                    LOGGER.info(
                            """
                                    当前标注@RpcService注解的类实例名称 ---> {}
                                    ==========================================================
                                    服务类 ---> {}
                                    服务类名 ---> {}
                                    版本 ---> {}
                                    分组 ---> {}""",
                            clazz.getName(),
                            rpcService.interfaceClass().getName(),
                            rpcService.interfaceName(),
                            rpcService.version(),
                            rpcService.group()
                    );
                    var serviceName = getServiceName(rpcService);
                    var key = RpcServiceUtils.buildServiceKey(serviceName, rpcService.version(), rpcService.group());
                    handlerMap.put(key, clazz.getDeclaredConstructor().newInstance());
                }
            } catch (Exception e) {
                LOGGER.error("scan classes not found: {}", className);
                // 不抛出异常，仅记录错误并继续处理其他类
            }
        }

        return handlerMap;
    }

    public static Map<String, Object> doScannerRpcServiceIntoRegistry(String host, int port, String scanPackage, RegistryService registryService) throws IOException {
        var handlerMap = new HashMap<String,Object>();
        var classNameList = getClassNameList(scanPackage);
        if (classNameList.isEmpty()) {
            return handlerMap;
        }
        classNameList.forEach(className -> {
            try {
                var clazz = Class.forName(className);
                var rpcService = clazz.getAnnotation(RpcService.class);
                if (Objects.nonNull(rpcService)) {
                    var serviceMeta = new ServiceMeta(getServiceName(rpcService), rpcService.version(), host, port, rpcService.group());
                    registryService.register(serviceMeta);

                    handlerMap.put(RpcServiceUtils.buildServiceKey(serviceMeta.getServiceName(),serviceMeta.getServiceVersion(),serviceMeta.getServiceGroup()), clazz.getDeclaredConstructor().newInstance());
                }
            } catch (Exception e) {
                LOGGER.error("scan classes throws ex ---> ", e);
            }
        });
        return handlerMap;
    }

    private static String getServiceName(RpcService rpcService) {
        if (StringUtils.isEmpty(rpcService.interfaceName()) && Objects.isNull(rpcService.interfaceClass())) {
            throw new IllegalArgumentException("RpcService not found config");
        }
        return Objects.nonNull(rpcService.interfaceClass()) ? rpcService.interfaceClass().getName() : rpcService.interfaceName();
    }

}
